/** @defgroup tool Tool
 * @{
 */

/*
 * Application\Src\tool.c
 *
 * Cargo tool function
 *
 * Author: Chao-Chi Kuo <chaochi_kuo@systech.com.tw>
 * Copyright (c) 2010 SYSTEMS & TECHNOLOGY CORP. <http://www.systech.com.tw/>
 * All rights reserved.
 *
 * Modifications:
 * 2009/11/18 Chao-Chi Kuo	first implemetation
 *
 * $Id: tool.c 250 2010-11-04 07:08:28Z joseph_hsu $
 */
#include "config.h"
#include "tool.h"
#include "type.h"
#include "xstring.h"
#include <stdlib.h>
#include <time.h>
#include <math.h>

#include "memory.h"


/**
 * @brief	converts a string to an hexadecimal
 * @param	buf	string
 * @return	hexadecimal number
 * @note	
 */
int atoh(char *buf)
{
	int ret = 0;
	unsigned char temp;
	
	while (*buf != '\0')
	{
		if ((*buf > 47) && (*buf < 58))	/* 0-9 */
		{
			temp = *buf - 48;
		}
		else if ((*buf > 64) && (*buf < 71)) /* A-F */
		{
			temp = *buf - 55;
		}
		else if ((*buf > 96) && (*buf < 103)) /* a-f */
		{
			temp = *buf - 87;
		}
		else
		{
			break;
		}
		ret <<= 4;
		ret |= temp;
		buf++;
	}
	return ret;
}

/**
 * @brief	converts a string to an interger
 * @param	buf	string
 * @return	decimal number
 * @note	Input "0x" as hexadecimal, ex. xatoi("0xA") = 10, xatoi("10") = 10
 */
int xatoi(char *buf)
{
	int ret;
	
	if (*buf == '0')
		buf++;
	if (*buf == 'x' || *buf == 'X')
	{	
		buf++;
		if (Chk_Hex_String(buf) == 0)
			ret = atoh(buf);
		else
			ret = -2147483648;
	}
	else
	{
		if (Chk_Num_String(buf) == 0)
			ret = atoi(buf);
		else
			ret = -2147483648;
	}
	return ret;
}

/**
 * @brief	converts a character to uppercase
 * @param	ch character
 * @return	uppercase version of the character ch
 * @note
 */
int xtoupper(int ch)
{
	if ((ch > 96) && (ch < 123))	/* a-z */
		ch -= 32;	/* a-z -> A-Z */
	return ch; 
}

/**
 * @brief	converts a string to a long long
 * @param	buf	string
 * @return	long long interger
 * @note	Input "0x" as hexadecimal, ex. xatoi("0xA") = 10, xatoi("10") = 10
 */
unsigned long long xatoll(char *buf)
{
	unsigned long long ret = 0;
	unsigned char temp;
	
	if (*buf == '0')
		buf++;
	if (*buf == 'x' || *buf == 'X')
	{	
		buf++;
		if (Chk_Hex_String(buf) == 0)
		{
			while (*buf != '\0')
			{
				if ((*buf > 47) && (*buf < 58))	/* 0-9 */
				{
					temp = *buf - 48;
				}
				else if ((*buf > 64) && (*buf < 71)) /* A-F */
				{
					temp = *buf - 55;
				}
				else if ((*buf > 96) && (*buf < 103)) /* a-f */
				{
					temp = *buf - 87;
				}
				else
				{
					break;
				}
				ret <<= 4;
				ret |= temp;
				buf++;
			}
		}
	}
	else
	{
		if (Chk_Num_String(buf) == 0)
		{
			while (*buf != '\0')
			{
				if ((*buf > 47) && (*buf < 58))	/* 0-9 */
				{
					temp = *buf - 48;
					ret *= 10;
				}
				else
				{
					break;
				}
				ret += temp;
				buf++;
			}
		}
	}
	
	return ret;
}

/**
 * @brief	calcute check sum of a string
 * @param	buf	string
 * @param	len	bytes of the string
 * @return	checksum 
 * @note	The checksum is the exclusive OR of input characters.
 */
unsigned char Calc_CheckSum(char *buf, unsigned short len)
{
	int i;
	unsigned char sum;
	char *pbuf;
	
	pbuf = buf;
	sum = 0;
	for (i = 0; i < len; i++)
		sum ^= *pbuf++;
	return sum;
}

/**
 * @brief	check all character of input string is decimal number("0 - 9") 
 * @param	buf	string
 * @return	0: True, 1: False
 * @note	
 */
int Chk_Num_String(char *buf)
{
	while ((*buf != '\0') && (*buf != ASC_CR))
	{
		if ((*buf < 0x30) || (*buf > 0x39))
		{
			if (!((*buf == '-') || (*buf == '+')))
				return 1;
		}
		buf++;
	}
	return 0;
}

/**
 * @brief	check all character of input string is hexadecimal number("0 - 9, a - f, A - F") 
 * @param	buf	string
 * @return	0: True, 1: False
 * @note	
 */
int Chk_Hex_String(char *buf)
{
	while (*buf != '\0')
	{
		if ((*buf >= 0x30) && (*buf <= 0x39))
			buf++;
		else if ((*buf >= 0x41) && (*buf <= 0x46))
			buf++;
		else if ((*buf >= 0x61) && (*buf <= 0x66))
			buf++;
		else
			return 1;
	}
	return 0;
}

/**
 * @brief	get a part string of input string. separate with ',' 
 * @param	buf		input string
 * @param	dest	output string
 * @param	idx		index of item
 * @return	0: Success, 1: Failed
 * @note	"123,456,789", item 0: "123", item 1: "456", item 0: "789",
 */
int GetStringItem(char *buf, char *dest, unsigned char idx)
{
	int i;
	
	for (i = 0; i < idx; i++)
	{
		buf = xstrchr(buf, ',');
		if (buf == NULL)
		{
			*dest = '\0';
			return -1;
		}
		buf++;
	}
	while ((*buf != ',') && (*buf != '\0') && (*buf != ASC_CR) && (*buf != ';'))
	{
		*dest++ = *buf++;
	}
	*dest = '\0';
	
	return 0;
}

/**
 * @brief	change date time to unix time
 * @param	dtime	date time
 * @return	unix time
 * @note
 */
unsigned long TimeToUnix(DATE_TIME dtime)
{
	struct tm *time;
	time_t ttime;
	
	time = (struct tm *)malloc(sizeof(struct tm));
	assert_malloc(time);
	xmemset(time, 0, sizeof(struct tm));
	time->tm_sec	= dtime.sec;
	time->tm_min	= dtime.min;
	time->tm_hour	= dtime.hour;
	time->tm_mday	= dtime.day;
	time->tm_mon	= dtime.month - 1;
	time->tm_year	= dtime.year - 1900;
	ttime = mktime(time);
	free(time);
	assert_free();
	return (unsigned long)ttime;
}

/**
 * @brief	change unix time to date time
 * @param	utime	unix time
 * @param	dtime	date time structure pointer
 * @return	none
 * @note
 */
void UnixToTime(unsigned long utime, DATE_TIME *dtime)
{
	struct tm *pt;
	time_t ttime;
	
	ttime = (time_t)utime;
	pt = gmtime(&ttime);
	dtime->sec		= pt->tm_sec;
	dtime->min		= pt->tm_min;
	dtime->hour		= pt->tm_hour;
	dtime->day		= pt->tm_mday;
	dtime->month	= pt->tm_mon + 1;
	dtime->year		= pt->tm_year + 1900;
}

/**
 * @brief	convert octet string to character  
 * @param	buf	octet string
 * @return	character
 * @note	only convert 2 byte, ex: "12" = 0x12, "12345" = 0x12 
 */
unsigned char octstr_get_char(char *buf)
{
	unsigned char hn = buf[0];
	unsigned char ln = buf[1];
	unsigned char byte;

	hn = (hn > '9') ? (hn - 'A' + 10) : (hn - '0');
	ln = (ln > '9') ? (ln - 'A' + 10) : (ln - '0');
	byte = (hn << 4) | ln;

	return byte;
}

/**
 * @brief	convert character to octet string  
 * @param	buf		dest string
 * @param	byte	character
 * @return	none
 * @note	0x12 = "12"
 */
void char_get_octstr(char *buf, unsigned char byte)
{
	unsigned char hn;
	unsigned char ln;
	
	hn = (byte >> 4);
	ln = (byte & 0x0F);
	
	buf[0] = (hn > 9) ? (hn - 10 + 'A') : (hn + '0');
	buf[1] = (ln > 9) ? (ln - 10 + 'A') : (ln + '0');
}

/**
 * @brief	check input buffer NULL or not 
 * @param	buf	input buffer
 * @return	none
 * @note	if buf == NULL, program will halt.
 */
int malloc_num = 0;
void assert_malloc(void *buf)
{
#if (DEBUG_MODE)
	malloc_num++;
	if (buf == NULL)
		while(1) {}
#endif
}

void assert_free(void)
{
#if (DEBUG_MODE)
	malloc_num--;
#endif
}


unsigned long CalcDistanceInMeter(double lat1, double long1, double lat2, double long2)
{  
	double d_radius, d_distance;
	unsigned long distmeter;
	
	if( (lat1 == lat2) && (long1 == long2))
	  	return 0;

	// 20090824 john say, ellis modify
	long1  = (double)((-1 * long1) * 0.017453292 );
	long2  = (double)((-1 * long2) * 0.017453292 );
	lat1   = (double)((90 - lat1) * 0.017453292 );
	lat2   = (double)((90 - lat2) * 0.017453292 );

	d_radius= 3959 * 1.609344; //Earth radius

	d_distance = d_radius * acos((sin(lat1) * cos(long1) * sin(lat2) * cos(long2)) +
	                             (sin(lat1) * sin(long1) * sin(lat2) * sin(long2)) +
	                             (cos(lat1) * cos(lat2)));
	distmeter = (unsigned long) (d_distance * 1000);
	
	return distmeter;
}

int Check_Time_up(unsigned long current_time, unsigned long chk_time)
{
	if (current_time < chk_time)
		return 0;
	else
		return 1;
}

int Check_Heading_Over(float head_1, float head_2, unsigned short th)
{
	unsigned short diff;
	if (head_1 > head_2)
	{
		diff = (int)(head_1 - head_2);
	}
	else
	{
		diff = (int)(head_1 + 360 - head_2);
	}
	
	if (diff > 180)
		diff = 360 - diff;
		
	if (diff > th)
		return 1;
	else
		return 0;
}

unsigned short GetADC1Channel(unsigned char chanel)
{
    // Configure channel
    ADC_RegularChannelConfig(ADC1, chanel, 1, ADC_SampleTime_7Cycles5);//ADC_SampleTime_55Cycles5);
    
    // Start the conversion
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    
    // Wait until conversion completion
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
    
    // Get the conversion value
    return ADC_GetConversionValue(ADC1);
}

/** @} */